package org.example.web;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.yc.damai.entity.DmOrderitem;
import com.yc.damai.entity.DmOrders;
import com.yc.damai.entity.DmProduct;
import com.yc.damai.mapper.DmOrderitemMapper;
import com.yc.damai.mapper.DmOrdersMapper;
import com.yc.damai.mapper.DmProductMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Company 源辰信息
 *
 * @author navy
 * @date 2024/6/28
 * Email haijunzhou@hnit.edu.cn
 */
@RestController
@RequestMapping("secKill")
public class SecKillDo {

    @Autowired
    DmProductMapper productMapper;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    TaskScheduler taskScheduler;
    @GetMapping("begin")
    @Transactional // 事务注解,官方建议写在业务层
    public String begin(){
        // TODO 秒杀对象要通过定时器加载数据库的记录
        SecKillInfo secKillInfo = new SecKillInfo();
        secKillInfo.beginTime = new Date();
        secKillInfo.endTime = new Date(secKillInfo.beginTime.getTime() + 1000 * 60 * 15);
        // TODO 启动定时任务, 在 end 时间结束秒杀
        Page<DmProduct> page = new PageDTO<>(1,10);
        LambdaQueryWrapper<DmProduct> lqw = new LambdaQueryWrapper<>();
        lqw.orderByDesc(DmProduct::getStore);
        Page<DmProduct> dmProductPage = productMapper.selectPage(page, lqw);
        secKillInfo.products = dmProductPage.getRecords();
        // TODO 批量更新 MySQL 商品数量
        secKillInfo.products.forEach(p->{
            p.setStore(p.getStore() - 100);
            productMapper.updateById(p);
        });

        ListOperations ops = redisTemplate.opsForList();
        // 保存商品详细信息
        ops.leftPushAll("secKillInfo.products", secKillInfo.products);
        // 保存商品id=>store 映射关系
        secKillInfo.products.forEach(p->{
            ValueOperations opsForValue = redisTemplate.opsForValue();
            opsForValue.set(p.getId(), 100);
        });

        redisTemplate.opsForValue().set("secKillEnd", secKillInfo.endTime.getTime());

        // 创建延时停止任务
        taskScheduler.schedule(()->{
            // 停止秒杀
            end();
        },secKillInfo.endTime);

        return "秒杀活动开始";
    }

    //@GetMapping("end")
    @Transactional
    public String end(){
        // TODO 通过定时器自动停止秒杀
        // TODO 在停止秒杀的过程, 如果接受秒杀请求, 如何处理

        // 查询数据库对应商品
        List<DmProduct> pRedis = redisTemplate.opsForList().range("secKillInfo.products", 0, -1);
        List<DmProduct> pMysql = productMapper.selectBatchIds(pRedis
                .stream()
                .map(p->p.getId())
                .collect(Collectors.toList()));

        pMysql.forEach(p->{
            // 查找 redis 中对应的商品
            Object ostore = redisTemplate.opsForValue().get(p.getId());
            int istore = Integer.parseInt(ostore+"");
            p.setStore(istore + p.getStore());
            productMapper.updateById(p);
        });

        redisTemplate.delete("secKillInfo.products");
        pMysql.forEach(p->{
            redisTemplate.delete(p.getId());
        });

        System.out.println("秒杀活动结束");
        return "秒杀活动结束";
    }

    @GetMapping("queryProduct")
    public Map<String,Object> queryProduct(){
        Map<String,Object> ret = new HashMap<>();
        ret.put("products", redisTemplate.opsForList().range("secKillInfo.products",0,-1));
        ret.put("endTime", redisTemplate.opsForValue().get("secKillEnd"));
        return ret;
    }

    @Autowired
    RabbitTemplate rabbitTemplate;

    // 业务规则: 同一个用同一个商品只能秒杀一次, 数量限制1个
    // TODO 返回结果应该是 Result 对象
    @RequestMapping("commit")
    public String commit(int uid, int pid){
        // TODO Redis 也有事务控制的问题  redis lua 脚本
        // redisTemplate.setEnableTransactionSupport(true);

        if ( ! redisTemplate.hasKey("secKillInfo.products")) {
            return "秒杀活动未开始!";
        }

        if (!redisTemplate.hasKey(pid)) {
            return "该商品未参与秒杀!";
        }

        // 判断该用户是否秒杀过该商品 => Redis => 集合 user:product => set  set get
        SetOperations set = redisTemplate.opsForSet();
        String key = uid + ":" + pid;
        if (set.isMember("secKillUserProduct",key)) {
            return "您已经秒杀过该商品了!";
        }
        // 判断商品数量
        ValueOperations value = redisTemplate.opsForValue();
        int number = (int) value.get(pid);
        if(number<=0){
            return "该商品秒杀完了!";
        }
        // 如果没有秒杀, 1. redis 减库存 -1,  2. 添加 user:product
        value.set(pid, number - 1);
        set.add("secKillUserProduct",key);

        // 发送订单消息到消息队列
        rabbitTemplate.convertAndSend("secKill", new SecKillOrder(uid,pid));
        // 返回结果
        return "秒杀成功!";
    }

    @Autowired
    DmOrdersMapper ordersMapper;
    @Autowired
    DmOrderitemMapper orderitemMapper;

    @Transactional
    @RabbitListener(queuesToDeclare = @Queue("secKill"))
    public void saveOrder(SecKillOrder killOrder){

        DmProduct dmProduct = productMapper.selectById(killOrder.pid);

        // 订单记录
        DmOrders orders = new DmOrders();
        orders.setUid(killOrder.uid);
        orders.setState(1); // 已支付
        orders.setTotal(dmProduct.getShopPrice()*1);
        ordersMapper.insert(orders); // 新增订单完成之后, MyBatis 会自动返回订单id

        // 订单明细
        DmOrderitem orderitem = new DmOrderitem();
        orderitem.setOid(orders.getId());
        orderitem.setPid(killOrder.pid);
        orderitem.setTotal(dmProduct.getShopPrice());
        orderitem.setCount(1);
        orderitemMapper.insert(orderitem);

        // 库存不用改

        System.out.println("秒杀订单保存成功! => TODO 发送短信(邮件)给用户!");

    }



}

@Data
class SecKillInfo{
    List<DmProduct> products;
    Date beginTime;
    Date endTime;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class SecKillOrder implements java.io.Serializable{
    int uid;
    int pid;
}

